{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "source": [
        "!pip -q install agentlightning openai nest_asyncio python-dotenv > /dev/null\n",
        "\n",
        "import os, threading, time, asyncio, nest_asyncio, random\n",
        "from getpass import getpass\n",
        "from agentlightning.litagent import LitAgent\n",
        "from agentlightning.trainer import Trainer\n",
        "from agentlightning.server import AgentLightningServer\n",
        "from agentlightning.types import PromptTemplate\n",
        "\n",
        "import openai\n",
        "if not os.getenv(\"OPENAI_API_KEY\"):\n",
        "    try:\n",
        "        os.environ[\"OPENAI_API_KEY\"] = getpass(\"🔑 Enter OPENAI_API_KEY (leave blank if using a local/proxy base): \") or \"\"\n",
        "    except Exception:\n",
        "        pass\n",
        "MODEL = os.getenv(\"MODEL\", \"gpt-4o-mini\")"
      ],
      "metadata": {
        "id": "QSqT6mZLX1q5"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "class QAAgent(LitAgent):\n",
        "    def training_rollout(self, task, rollout_id, resources):\n",
        "        \"\"\"Given a task {'prompt':..., 'answer':...}, ask LLM using the server-provided system prompt and return a reward in [0,1].\"\"\"\n",
        "        sys_prompt = resources[\"system_prompt\"].template\n",
        "        user = task[\"prompt\"]; gold = task.get(\"answer\",\"\").strip().lower()\n",
        "        try:\n",
        "            r = openai.chat.completions.create(\n",
        "                model=MODEL,\n",
        "                messages=[{\"role\":\"system\",\"content\":sys_prompt},\n",
        "                          {\"role\":\"user\",\"content\":user}],\n",
        "                temperature=0.2,\n",
        "            )\n",
        "            pred = r.choices[0].message.content.strip()\n",
        "        except Exception as e:\n",
        "            pred = f\"[error]{e}\"\n",
        "        def score(pred, gold):\n",
        "            P = pred.lower()\n",
        "            base = 1.0 if gold and gold in P else 0.0\n",
        "            gt = set(gold.split()); pr = set(P.split());\n",
        "            inter = len(gt & pr); denom = (len(gt)+len(pr)) or 1\n",
        "            overlap = 2*inter/denom\n",
        "            brevity = 0.2 if base==1.0 and len(P.split())<=8 else 0.0\n",
        "            return max(0.0, min(1.0, 0.7*base + 0.25*overlap + brevity))\n",
        "        return float(score(pred, gold))"
      ],
      "metadata": {
        "id": "FSryTgtjX56u"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "TASKS = [\n",
        "    {\"prompt\":\"What is the capital of France?\",\"answer\":\"Paris\"},\n",
        "    {\"prompt\":\"Who wrote Pride and Prejudice?\",\"answer\":\"Jane Austen\"},\n",
        "    {\"prompt\":\"2+2 = ?\",\"answer\":\"4\"},\n",
        "]\n",
        "\n",
        "PROMPTS = [\n",
        "    \"You are a terse expert. Answer with only the final fact, no sentences.\",\n",
        "    \"You are a helpful, knowledgeable AI. Prefer concise, correct answers.\",\n",
        "    \"Answer as a rigorous evaluator; return only the canonical fact.\",\n",
        "    \"Be a friendly tutor. Give the one-word answer if obvious.\"\n",
        "]\n",
        "\n",
        "nest_asyncio.apply()\n",
        "HOST, PORT = \"127.0.0.1\", 9997"
      ],
      "metadata": {
        "id": "7UP8VdYAYC_C"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "async def run_server_and_search():\n",
        "    server = AgentLightningServer(host=HOST, port=PORT)\n",
        "    await server.start()\n",
        "    print(\"✅ Server started\")\n",
        "    await asyncio.sleep(1.5)\n",
        "\n",
        "    results = []\n",
        "    for sp in PROMPTS:\n",
        "        await server.update_resources({\"system_prompt\": PromptTemplate(template=sp, engine=\"f-string\")})\n",
        "        scores = []\n",
        "        for t in TASKS:\n",
        "            tid = await server.queue_task(sample=t, mode=\"train\")\n",
        "            rollout = await server.poll_completed_rollout(tid, timeout=40)  # waits for a worker\n",
        "            if rollout is None:\n",
        "                print(\"⏳ Timeout waiting for rollout; continuing...\")\n",
        "                continue\n",
        "            scores.append(float(getattr(rollout, \"final_reward\", 0.0)))\n",
        "        avg = sum(scores)/len(scores) if scores else 0.0\n",
        "        print(f\"🔎 Prompt avg: {avg:.3f}  |  {sp}\")\n",
        "        results.append((sp, avg))\n",
        "\n",
        "    best = max(results, key=lambda x: x[1]) if results else (\"<none>\",0)\n",
        "    print(\"\\n🏁 BEST PROMPT:\", best[0], \" | score:\", f\"{best[1]:.3f}\")\n",
        "    await server.stop()"
      ],
      "metadata": {
        "id": "s4weFPCVYFlL"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Lh6AJtGjUHEV",
        "outputId": "d83fb516-9712-4490-d5d6-0769c028be44"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "🔑 Enter OPENAI_API_KEY (leave blank if using a local/proxy base): ··········\n",
            " * Serving Flask app 'agentlightning.instrumentation.agentops'\n",
            " * Debug mode: off\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:\u001b[31m\u001b[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\u001b[0m\n",
            " * Running on http://127.0.0.1:33465\n",
            "INFO:werkzeug:\u001b[33mPress CTRL+C to quit\u001b[0m\n",
            "INFO:     Started server process [698]\n",
            "INFO:     Waiting for application startup.\n",
            "INFO:     Application startup complete.\n",
            "INFO:     Uvicorn running on http://127.0.0.1:9997 (Press CTRL+C to quit)\n",
            "/usr/local/lib/python3.12/dist-packages/agentlightning/instrumentation/__init__.py:53: UserWarning: litellm is not installed. It's therefore not instrumented.\n",
            "  warnings.warn(\"litellm is not installed. It's therefore not instrumented.\")\n",
            "/usr/local/lib/python3.12/dist-packages/agentlightning/instrumentation/__init__.py:60: UserWarning: vllm is not installed. It's therefore not instrumented.\n",
            "  warnings.warn(\"vllm is not installed. It's therefore not instrumented.\")\n",
            "/usr/local/lib/python3.12/dist-packages/agentlightning/instrumentation/__init__.py:53: UserWarning: litellm is not installed. It's therefore not instrumented.\n",
            "  warnings.warn(\"litellm is not installed. It's therefore not instrumented.\")\n",
            "/usr/local/lib/python3.12/dist-packages/agentlightning/instrumentation/__init__.py:60: UserWarning: vllm is not installed. It's therefore not instrumented.\n",
            "  warnings.warn(\"vllm is not installed. It's therefore not instrumented.\")\n",
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:02:55] \"POST /v3/auth/token HTTP/1.1\" 200 -\n",
            "🖇 AgentOps: \u001b[34mYou're on the agentops free plan 🤔\u001b[0m\n",
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:02:55] \"POST /v3/auth/token HTTP/1.1\" 200 -\n",
            "🖇 AgentOps: \u001b[34mYou're on the agentops free plan 🤔\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "✅ Server started\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "🖇 AgentOps: [OPENAI INSTRUMENTOR] Error setting up OpenAI streaming wrappers: No module named 'openai.resources.beta.chat'\n",
            "🖇 AgentOps: [OPENAI INSTRUMENTOR] Error setting up OpenAI streaming wrappers: No module named 'openai.resources.beta.chat'\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:60952 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:60964 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:60972 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:60980 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:60992 - \"GET /resources/latest HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:56472 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:56476 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:02] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:56482 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:56494 - \"GET /resources/latest HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:56508 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:56520 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:56524 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:07] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:33310 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:33322 - \"GET /resources/latest HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:33324 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:33332 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:33340 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:12] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "🔎 Prompt avg: 0.233  |  You are a terse expert. Answer with only the final fact, no sentences.\n",
            "INFO:     127.0.0.1:33356 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:33360 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:33376 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:33382 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:33398 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:17] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:36490 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:36506 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:36510 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:36516 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:36524 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:22] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:36530 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:36536 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:36540 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:36548 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:36562 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:27] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "🔎 Prompt avg: 0.233  |  You are a helpful, knowledgeable AI. Prefer concise, correct answers.\n",
            "INFO:     127.0.0.1:39548 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:39560 - \"GET /resources/latest HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:39564 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:39576 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:39590 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:32] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:39602 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:39610 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:39626 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:39634 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:39644 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:37] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:47618 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:47624 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:47640 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:47656 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:47664 - \"GET /task HTTP/1.1\" 200 OK\n",
            "🔎 Prompt avg: 0.233  |  Answer as a rigorous evaluator; return only the canonical fact.\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:42] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:47676 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:47680 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:47688 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:47694 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:47706 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:47] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:34614 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:34624 - \"GET /resources/latest HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:34636 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:34652 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:34666 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:53] \"POST /traces HTTP/1.1\" 200 -\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:34670 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:34682 - \"GET /resources/latest HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[31;1m🖇 AgentOps: [OPENAI WRAPPER] Error in chat_completion_stream_wrapper: Error code: 401 - {'error': {'message': \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\", 'type': 'invalid_request_error', 'param': None, 'code': None}}\u001b[0m\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "INFO:     127.0.0.1:34688 - \"POST /rollout HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:34700 - \"GET /task HTTP/1.1\" 200 OK\n",
            "INFO:     127.0.0.1:34710 - \"GET /task HTTP/1.1\" 200 OK\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "INFO:     Shutting down\n",
            "INFO:werkzeug:127.0.0.1 - - [31/Aug/2025 09:03:58] \"POST /traces HTTP/1.1\" 200 -\n",
            "INFO:     Waiting for application shutdown.\n",
            "INFO:     Application shutdown complete.\n",
            "INFO:     Finished server process [698]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "🔎 Prompt avg: 0.233  |  Be a friendly tutor. Give the one-word answer if obvious.\n",
            "\n",
            "🏁 BEST PROMPT: You are a terse expert. Answer with only the final fact, no sentences.  | score: 0.233\n"
          ]
        }
      ],
      "source": [
        "def run_client_in_thread():\n",
        "    agent = QAAgent()\n",
        "    trainer = Trainer(n_workers=2)\n",
        "    trainer.fit(agent, backend=f\"http://{HOST}:{PORT}\")\n",
        "\n",
        "client_thr = threading.Thread(target=run_client_in_thread, daemon=True)\n",
        "client_thr.start()\n",
        "asyncio.run(run_server_and_search())"
      ]
    }
  ]
}